#!/usr/bin/env python
#-*- coding: utf-8 -*-
#Using GPL v3
#Author: stuyorks@gmail.com
#Version: 0.1



import urllib2
import re
import sys
import os
import mimetools
import mimetypes
import optparse
import getpass

from time import sleep

domain = "bbs.stu.edu.cn"
#the utmpXXX when login the javeScript will write to Cookie AND
#everyThing we do in BBS need Cookie. So, we must save it with global. 
utmpnum=""
utmpkey=""
utmpuserid=""

#while debug is TRUE, it will record send msg and response msg to 
#the file:./stu_BBS_upload.log
debug=False


def debugPrint(tag, content, filePath="./stu_BBS_upload.log"):
    if debug:
        writeContent=tag+content+"======================================="+os.linesep
        fileObj = open(filePath, 'a')
        fileObj.write(writeContent)
        fileObj.close()
    else:
        pass

def bbsLogin(bbsid, password):
    """
    bbsLogin function is use the urllib2 module to GET in the BBS.STU.EDU.CN
    arg bbsid and password are the str obj.

    return True if login successfull else return False.
    """

    loginHeaders = {
                    'Host':'%s' % domain,
                    'User-Agent':'Mozilla/5.0 (X11; U; Linux i686; zh-CN; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5',
                    'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                    'Accept-Language':'zh-cn,zh;q=0.5',
                    'Accept-Encoding':'gzip,deflate',
                    'Accept-Charset':'GB2312,utf-8;q=0.7,*;q=0.7',
                    'Keep-Alive':'300',
                    'Connection':'keep-alive',
                    'Referer':'http://%s/' % domain,
                    'Cookie':'utmpnum=; utmpkey=; utmpuserid=; my_t_lines=; my_link_mode=; my_def_mode=; ystat_bc_814849=989653192219184857',
                    'Content-type':'application/x-www-form-urlencoded',
             }
    loginURL='http://%s/cgi-bin/bbslogin?id=%s&pw=%s' % (domain, bbsid, password)
    debugPrint("login SEND:\n", loginURL+os.linesep+str(loginHeaders))

    request = urllib2.Request(url=loginURL,headers=loginHeaders)
    try:
        conn = urllib2.urlopen(request)
    except urllib2.URLError, e:
        print e.reason
        return False
 
    header = str(conn.info())
    
    res = unicode(conn.read(),'gb2312').encode('utf-8')
    debugPrint("login RESPONSE:\n", header+res)

    global utmpnum, utmpkey, utmpuserid
    try:
        utmpnum = re.findall(r"document.cookie='utmpnum=(.*)'</script>",res,re.M)[0]
        utmpkey = re.findall(r"document.cookie='utmpkey=(.*)'</script>",res,re.M)[0]
        utmpuserid = re.findall(r"document.cookie='utmpuserid=(.*)'</script>",res,re.M)[0]
        debugPrint("login GET utmpnum,utmpkey,utmpuserid:\n", utmpnum+","+utmpkey+","+utmpuserid)        
    except:
        return False
    return True


def bbsLogout():
    """
     bbsLogout function is use the urllib2 module to POST DATA to logout.
     
     logoutHeaders: the Cookie segment ystat_bc_...MUST the same with login's!
    """
    logoutHeaders = {
                      'Host':'%s' % domain,
                      'User-Agent':'Mozilla/5.0 (X11; U; Linux i686; zh-CN; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5',
                      'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                      'Accept-Language':'zh-cn,zh;q=0.5',
                      'Accept-Encoding':'gzip,deflate',
                      'Accept-Charset':'GB2312,utf-8;q=0.7,*;q=0.7',
                      'Keep-Alive':'300',
                      'Connection':'keep-alive',
                      'Referer':'http://%s/cgi-bin/bbsleft' % domain,
                      'Cookie':'utmpnum=%s; utmpkey=%s; utmpuserid=%s; my_t_lines=; my_link_mode=; my_def_mode=; ystat_bc_814849=989653192219184857' % (utmpnum, utmpkey, utmpuserid),
                  } 
    logoutURL = 'http://%s/cgi-bin/bbslogout' % domain 
    debugPrint("logout SEND:\n", logoutURL+os.linesep+str(logoutHeaders))

    request = urllib2.Request(url=logoutURL,headers=logoutHeaders)
    conn = urllib2.urlopen(request)
    header = str(conn.info())
    res = unicode(conn.read(),'gb2312').encode('utf-8') 
    debugPrint("logout RESPONSE:\n", header+res)

def postArticle(board, title, content):

    postContent = "title=%s&icon=%s&signature=%s&isAttach=0&anonymous=N&exchange=N&text=%s" % (title, '0', '1',content)+"""%0D%0A"""

    postHeaders = {
             'Host':'%s' % domain,
             'User-Agent':'Mozilla/5.0 (X11; U; Linux i686; zh-CN; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5',
             'Accept':'text/html,mage/gif,image/x-xbitmap,image/jpeg,image/pjpeg,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
             'Accept-Language':'zh-cn,zh;q=0.5',
             'Accept-Encoding':'gzip,deflate',
             'Accept-Charset':'GB2312,utf-8;q=0.7,*;q=0.7',
             'Keep-Alive':'300',
             'Connection':'keep-alive',
             'Referer':'http://%s/cgi-bin/bbspst?board=%s' % (domain, board), 
             'Cookie':'utmpnum=%s; utmpkey=%s; utmpuserid=%s; my_t_lines=; my_link_mode=; my_def_mode=; ystat_bc_814849=989653192219184857' %
(utmpnum, utmpkey, utmpuserid),
             'Content-type':'application/x-www-form-urlencoded',
             'Content-Length':'%s' % len(postContent),
            }

    postURL = "http://%s/cgi-bin/bbssnd?board=%s" % (domain, board)
    debugPrint("postArticle SEND:\n", postURL+os.linesep+str(postHeaders)+os.linesep+postContent)

    request = urllib2.Request(url=postURL,headers=postHeaders, data=postContent)
    conn = urllib2.urlopen(request)
    header = str(conn.info())
    res = unicode(conn.read(),'gb2312').encode('utf-8')
    debugPrint("postArticle RESPONSE:\n", header+res)


def uploadPicture(picturePath):
    """
    uploadPicture function is to upload a file to BBS.STU.EDU.CN by urllib2 module.
    The arg picturePath is the FULL PATH of the file.
     
    return: True if upload successful else return False
    """
    #fileSize = os.path.getsize(picturePath)
    #print fileSize
    #if 524288 < fileSize:
        #return "bigSize"


    fileObj=open(picturePath,"rb")
    fileContent=fileObj.read()
    fileObj.close()

    #to compare with wirshark package more similarity, I define it is a const..
    #but use mimetools is a good method.
    BOUNDARY = "---------------------------7d91f129901cc"
    #BOUNDARY = mimetools.choose_boundary()
    #print BOUNDARY
    
    fileBaseName = os.path.basename(picturePath)
    #BBS upload file CGI program May use '\' to get FILENAME,
    #So, its format MUST like this: '\yorks.jpg'!
    fileName=r'V:\yorks\%s' % fileBaseName

    CRLF = '\r\n'
    bodyList=[]
    bodyList.extend(
    ['--' + BOUNDARY,
     'Content-Disposition: form-data; name="upload_file"; filename="%s"' % fileName,
     'Content-Type: %s' % mimetypes.guess_type(picturePath)[0],
     '',
     fileContent,
    ])
    bodyList.extend(['--' + BOUNDARY + '--', ''])

    body=CRLF.join(bodyList)

    uploadHeaders = {
                      'Accept':'image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint, */*',
                      'Referer':'http://%s/cgi-bin/fileupload' % domain,
                      'Accept-Language':'zh-cn',
                      'Content-Type':'multipart/form-data; boundary=%s' % BOUNDARY,
                      'Accept-Encoding':'gzip, deflate',
                      'User-Agent':'Mozilla/5.0 (X11; U; Linux i686; zh-CN; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5',
                      'Host': '%s' % domain,
                      'Content-Length':'%s' % len(body),
                      'Connection':'Keep-Alive',
                      'Cache-Control':'no-cache',
                      'Cookie':'utmpnum=%s; utmpkey=%s; utmpuserid=%s' % (utmpnum, utmpkey, utmpuserid),
                   }

    uploadURL = "http://%s/cgi-bin/fileupload?job=add" % domain 
    debugPrint("uploadPic SEND except BODY:\n", uploadURL+os.linesep+str(uploadHeaders))

    request = urllib2.Request(url=uploadURL,headers=uploadHeaders, data=body)
    try:
        conn = urllib2.urlopen(request)
    except urllib2.URLError, e:
        print e.reason
        return False
    header = str(conn.info())
    res = unicode(conn.read(),'gb2312').encode('utf-8')
    debugPrint("uploadPicture RESPONSE:\n", header+res)

    if -1 != res.find(fileBaseName):
        return True
    else:
        return False

    
def parserHandle():
    """
    parserHandle function is use optparse module to get the files which want to upload with args
    get BBSid, password in the options. IF -u or -p is not provided it will need to input by raw_input.
    IF the files are not provided it will exit.

    return id: bbsID (string)
    return pw: password (string)
    return args: the files want to upload (list)
    """
    usage="usage: %prog file1 [file2...] [Options]"
    description="%prog is a client for upload picture to (bbs.stu.edu.cn). Especially it's for STU Linux users, who have trouble in upload pictures with firefox!\nIt is a free software under the GPLv3. Report bugs to: yorks<stuyorks@gmail.com>"
    version="%prog 0.1"
    parser = optparse.OptionParser(usage=usage,description=description,version=version)
    parser.add_option('-i', '--id', dest='bbsID', help='Your STU BBS ID')
    parser.add_option('-p', '--password', dest='password',help='Your STU BBS password')
    parser.add_option('-d', '--debug', action="store_true", default=False, dest='debug',help='It is a switch that if use -d the program works in DEBUG model! this may write the msg to stu_BBS_upload.log file.')
    options, args = parser.parse_args()
    global debug
    debug = options.debug
    debugPrint("Upload File list NUM any name:\n", str(len(args))+","+str(args))

    if len(args) < 1:
        parser.error('picture FULL PATH to upload not provided.')
    
    for i in args:
        if not os.path.isfile(i):
            print "%s文件不存在或者找不到!" % i
            exit(1)

    # handle the file size > 512K
    # It will get all the files size, when there is one>512K or more
    # it will exit 1.
    bigFiles = []
    for picturePath in args:
        fileSize = os.path.getsize(picturePath)
        if 524288 < fileSize:
            print "文件:%s大于512K !" % picturePath
            bigFiles.append(picturePath)
    if 0 != len(bigFiles):
        print "请缩小图片再重新运行本程序！"
        exit(1)

    if not options.bbsID:
        id = raw_input("请输入您的BBS ID:")
    else:
        id = options.bbsID
    if not options.password:
        pw = getpass.getpass("请输入您的密码:")
    else:
        pw = options.password
    return id, pw, args
                 
def main():

    bbsID, password, picturePathList = parserHandle()


    #input id and pw to avoid login again in a short time
    #bbsID=raw_input("bbs ID:")
    #password = raw_input("password:")
    
   
    isLogin = bbsLogin(bbsID, password)
    if not isLogin:
        print "登录失败，请检查用户名密码以及网络情况!"
        exit(1)

    #have twice to upload if the file upload fails.
    #May be there have another GOOD mothods! :-D
    failFiles = []
    for picturePath in picturePathList:
        #try:
        result = uploadPicture(picturePath)
        if result:
            print "%s上传成功!" % picturePath
        #except:
            #print "%s上传失败!" % picturePath
            #failFiles.append(picturePath)
            #continue
    if 0 != len(failFiles):
        print "重试上传失败文件中"
        for fail in failFiles:
            try:
                result = uploadPicture(fail)
                if result:
                    print "%s上传成功!" % fail
            except:
                print "%s还是上传失败!" % fail
                continue
    else:
        print "所有图片已经上传完毕\n请到BBS的picture版发文即可关联到您的文章!"
        
        
    #picture have uploaded, but you want to go the BBS Picture board to post an article,
    #than the pictures will be included in the article. BUT avoid POST MASS water to BBS,
    #I recommend that DO NOT use it to post article by this script.
    #board="Picture"
    #title="python-picture-uploader"
    #content="this is use python to upload PIC"
    #try:
        #postArticle(board, title, content)
    #except:
       #pass
    #avoid in server in a short time

    print "请等待，现在正在注销并退出!"
    sleep(10)
    bbsLogout()
    print "已经注销!"


if __name__ == "__main__":
    main()
       
